// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using svs.Mobile.BeyondMedia.FeedCore.ID3v2;

namespace svs.Mobile.BeyondMedia.FeedCore
{
	public class Track
	{
		#region DownloadStatuses enum

		public enum DownloadStatuses
		{
			Unknown,
			Connecting,
			Downloading,
			Downloaded,
			Stopped,
			Error
		}

		#endregion

		#region StreamType enum

		public enum StreamType
		{
			LocalFile,
			InternetStream,
			RSSFeed,
			Unknown
		}

		#endregion

		#region PlayMode enum

		public enum PlayMode
		{
			Pause,
			Play,
			Stop
		}

		#endregion

		private static readonly List<string> m_InternalPlayerExtensions = new List<string>();

		private static int INSTANCE_COUNTER = 0;
		private int m_CurrentInstance;
		private bool m_Deleted = false;
		private long m_DownloadedPortion = 0;
		private long m_DownloadSize = 0;
		private DownloadStatuses m_DownloadStatus = DownloadStatuses.Unknown;
		private DateTime? m_LastModifiedDate;
		private bool m_Modified = false;
		private Feed m_ParentFeed;
		private string m_Path;
		private bool m_Played = false;
		private double m_PlayedTime;
		private string m_Protocol = string.Empty;
		private int m_SortOrder = 10000;
		private double m_TotalTime;
		private string m_trackName = null;
		private string m_Url;
		private PlayMode m_CurrentPlayState = PlayMode.Stop;

		#region Events

		public event CoreHelper.TrackChangedEvent TrackChanged;

		#endregion

		#region Constructors

		static Track()
		{
			ReloadSupportedFileTypes();
		}

		public Track(string i_file, Feed i_ParentFeed)
		{
			m_CurrentInstance = INSTANCE_COUNTER++;
			m_Path = i_file;
			m_TotalTime = 0;
			m_PlayedTime = 0;
			m_ParentFeed = i_ParentFeed;
			m_Modified = false;
			m_Protocol = string.Empty;
			m_Played = false;
		}

		public Track(FileInfo i_file, Feed i_ParentFeed) : this(i_file.FullName, i_ParentFeed)
		{
			m_LastModifiedDate = i_file.LastWriteTime;
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Marks the track as played
		/// </summary>
		public void MarkPlayed()
		{
			if (TotalTime == 0) // the track has not been played yet
			{
				m_Played = true;
				MarkModified();
			}
			else
				PlayedTime = TotalTime;
		}

		/// <summary>
		/// Clears the track as played
		/// </summary>
		public void ClearPlayed()
		{
			PlayedTime = 0;
			m_Played = false;
		}

		/// <summary>
		/// Deletes the undelining file and marks this instance as Deleted.
		/// </summary>
		public void Delete()
		{
			File.Delete(TrackPath);
			m_Deleted = true;
		}

		public override string ToString()
		{
			return String.Format("Track: {0} ({1}/{2})", Name, TotalTimeAsString, PlayedTimeAsString);
		}

		public void LoadTrackNameFromFile()
		{
			if (Exists)
			{
				ID3Info reader = new ID3Info(TrackPath);
				if(!string.IsNullOrEmpty(reader.TagTitle))
					Name = reader.TagTitle;
			}
		}

		public static void ReloadSupportedFileTypes()
		{
			m_InternalPlayerExtensions.Clear();

			if (String.IsNullOrEmpty(Configuration.InternalPlayerSupportedFileExtensions))
				return;

			char[] dels = { ',' };
			string[] extensions = Configuration.InternalPlayerSupportedFileExtensions.ToLower().Split(dels);

			foreach (string s in extensions)
			{
				String ext = s.Trim();

				if (!string.IsNullOrEmpty(ext))
				{
					m_InternalPlayerExtensions.Add(ext.StartsWith(".") ? ext : "." + ext);
				}
			}
		}

		#endregion

		#region Public Properties

		public bool WillUseInternalPlayer
		{
			get { return SupportedFileType && ParentFeed.CurrentFeedPlayer == PlayerIntegration.UseInternalPlayer; }
		}

		/// <summary>
		/// Gets a value indicating whether this instance is deleted.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is deleted; otherwise, <c>false</c>.
		/// </value>
		public bool IsDeleted
		{
			get { return m_Deleted; }
		}

		/// <summary>
		/// Gets a value indicating whether this instance can be played by the internal player.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance can play internally; otherwise, <c>false</c>.
		/// </value>
		public bool SupportedFileType
		{
			get { return m_InternalPlayerExtensions.Contains(Path.GetExtension(m_Path).ToLower()); }
		}

		public Feed ParentFeed
		{
			get { return m_ParentFeed; }
			set { m_ParentFeed = value; }
		}

		/// <summary>
		/// Gets or sets the track path.
		/// </summary>
		/// <value>The track path.</value>
		public string TrackPath
		{
			get { return m_Path; }
			set { m_Path = value; }
		}

		/// <summary>
		/// Gets the date the track was written to the device
		/// </summary>
		/// <value>The last modified date.</value>
		public DateTime? LastModifiedDate
		{
			get
			{
				if (!m_LastModifiedDate.HasValue && Exists)
				{
					m_LastModifiedDate = File.GetLastWriteTime(TrackPath);
				}
				return m_LastModifiedDate;
			}
			set { m_LastModifiedDate = value; }
		}

		/// <summary>
		/// Gets the name of the track.
		/// </summary>
		/// <value>The name.</value>
		public string Name
		{
			get
			{
				if (m_trackName == null)
				{
					m_trackName = GetDefaultTrackName();
				}

				return m_trackName;
			}
			set
			{
				m_trackName = value;
				MarkModified();
			}
		}

		public bool HasGeneratedName
		{
			get
			{
				return string.IsNullOrEmpty(m_trackName) || m_trackName == GetDefaultTrackName();
			}
		}

		string GetDefaultTrackName()
		{
			string  trackName = Path.GetFileNameWithoutExtension(TrackPath).Replace("_", " ");
			if (!SupportedFileType)
			{
				string extension = Path.GetExtension(TrackPath);
				if(string.IsNullOrEmpty(extension) && extension.Length > 1)
					trackName += string.Format(" ({0})", extension.Substring(1));
			}

			return trackName;
		}

		/// <summary>
		/// Gets or sets the total time of the track.
		/// </summary>
		/// <value>The total time.</value>
		public int SortOrder
		{
			get { return m_SortOrder; }
			set
			{
				m_SortOrder = value;
				MarkModified();
			}
		}

		/// <summary>
		/// Gets or sets the total time of the track.
		/// </summary>
		/// <value>The total time.</value>
		public double TotalTime
		{
			get { return m_TotalTime; }
			set
			{
				m_TotalTime = value;
				MarkModified();
			}
		}

		/// <summary>
		/// Gets a value indicating whether this was modified.
		/// </summary>
		/// <value><c>true</c> if modified; otherwise, <c>false</c>.</value>
		public bool IsModified
		{
			get { return m_Modified; }
		}

		/// <summary>
		/// True if the track has nvere been played
		/// </summary>
		public bool IsNew
		{
			get { return !IsPlayed && (TotalTime == 0 || TotalTime > 0 && PlayedTime == 0); }
		}

		/// <summary>
		/// True if the track has been partially played
		/// </summary>
		public bool IsPartialyPlayed
		{
			get { return !IsNew && !IsPlayed; }
		}

		/// <summary>
		/// True if the track has been completely played
		/// </summary>
		public bool IsPlayed
		{
			get { return m_Played || TotalTime > 0 && (TotalTime - PlayedTime) < 5; }
		}

		/// <summary>
		/// Gets the total time as string.
		/// </summary>
		/// <value>The total time as string.</value>
		public string TotalTimeAsString
		{
			get { return CoreHelper.FormatAsTimeString(TotalTime); }
		}

		/// <summary>
		/// Gets or sets the played time of the track.
		/// </summary>
		/// <value>The played time.</value>
		public double PlayedTime
		{
			get { return m_PlayedTime; }
			set
			{
				m_PlayedTime = value;
				MarkModified();
			}
		}

		/// <summary>
		/// Gets the played time as a fraction (percent) of the total track time.
		/// </summary>
		/// <value>The percent played.</value>
		public float PlayedAsFraction
		{
			get
			{
				if (IsPlayed)
					return 1;

				else if (IsNew)
					return 0;
				else
				{
					return (float) (PlayedTime/TotalTime);
				}
			}
		}

		/// <summary>
		/// Gets the played time as string.
		/// </summary>
		/// <value>The played time as string.</value>
		public string PlayedTimeAsString
		{
			get { return CoreHelper.FormatAsTimeString(PlayedTime); }
		}

		/// <summary>
		/// Gets or sets the protocol that is used to access the stream (http, file mmst etc.).
		/// </summary>
		/// <value>The protocol.</value>
		public string Protocol
		{
			get { return m_Protocol; }
			set
			{
				m_Protocol = value;
				MarkModified();
			}
		}

		/// <summary>
		/// Gets a value indicating whether this Track is exists on the disk.
		/// </summary>
		/// <value><c>true</c> if exists; otherwise, <c>false</c>.</value>
		public bool Exists
		{
			get
			{
				if (string.IsNullOrEmpty(TrackPath))
					return false;

				return File.Exists(TrackPath);
			}
		}

		/// <summary>
		/// Gets the type the network type of the stream.
		/// </summary>
		/// <value>The type of the track.</value>
		public StreamType TrackType
		{
			get
			{
				if (Protocol.Length == 0)
					return StreamType.Unknown;
				else if (Protocol == "file")
					return StreamType.LocalFile;
				else if (Protocol == "rss")
					return StreamType.RSSFeed;
				else
					return StreamType.InternetStream;
			}
		}

		/// <summary>
		/// True if the track can be deleted automattcally
		/// </summary>
		public bool AllowAutoDelete
		{
			get
			{
				if (!IsFullyDownloaded)
					return false;

				return m_ParentFeed.CanDeleteTracks;
			}
		}

		/// <summary>
		/// The URL where this track was downloaded
		/// </summary>
		public string Url
		{
			get { return m_Url; }
			set
			{
				if (m_Url != value)
				{
					m_Url = value;
					MarkModified();
				}
			}
		}

		/// <summary>
		/// The total size of the file to be downloaded
		/// </summary>
		public long DownloadSize
		{
			get { return m_DownloadSize; }
			set
			{
				if (m_DownloadSize != value)
				{
					m_DownloadSize = value;
					MarkModified();
				}
			}
		}

		/// <summary>
		/// The portion of the file cuurently downloaded
		/// </summary>
		public long DownloadedPortion
		{
			get { return m_DownloadedPortion; }
			set
			{
				if (m_DownloadedPortion != value)
				{
					m_DownloadedPortion = value;
					MarkModified();
				}
			}
		}

		/// <summary>
		/// Percent of file downloaded
		/// </summary>
		public decimal DownloadPercent
		{
			get
			{
				if (DownloadedPortion == 0 || DownloadSize == 0)
					return 0;

				return ((decimal) DownloadedPortion/(decimal) DownloadSize)*100;
			}
		}

		/// <summary>
		/// Tru if the fille is fully downloaded
		/// </summary>
		public bool IsFullyDownloaded
		{
			get
			{
				//The Local File must exist
				if (Exists)
				{
					if (string.IsNullOrEmpty(Url)) // if this is a local file only 
						return true;

					//The file has an URL
					if (DownloadSize > 0 && DownloadedPortion == DownloadSize)
						return true;
				}

				return false;
			}
		}

		/// <summary>
		/// True if the track has a URL
		/// </summary>
		public bool HasUrl
		{
			get { return !string.IsNullOrEmpty(Url); }
		}

		/// <summary>
		/// True if the file is currently downloading
		/// </summary>
		public DownloadStatuses DownloadStatus
		{
			get { return m_DownloadStatus; }
			set { m_DownloadStatus = value; }
		}

		public PlayMode CurrentPlayState
		{
			get { return m_CurrentPlayState; }
			set
			{
				if (m_CurrentPlayState != value)
				{
					m_CurrentPlayState = value;
					if (TrackChanged != null)
						TrackChanged(this);
				}
			}
		}

		#endregion

		#region Private Methods
		/// <summary>
		/// Marks a track as modified
		/// </summary>
		private void MarkModified()
		{
			m_Modified = true;
			if (TrackChanged != null)
				TrackChanged(this);
		}

		#endregion
	}
}